package com.logic.modular.game.player.bag.service;

import cn.hutool.core.lang.UUID;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.comment.dubbo.service.bag.BagItemService;
import com.comment.util.pay.Pledge;
import com.comment.exception.ServiceException;
import com.logic.comment.util.BeanUtil;

import com.logic.comment.util.RedisUtils;
import com.logic.config.netty.web.service.MessageService;
import com.logic.modular.game.GameConstant;
import com.comment.pojo.domain.bag.Bag;
import com.comment.pojo.domain.bag.BagItem;
import com.logic.modular.game.player.bag.mapper.BagItemMapper;
import com.comment.pojo.domain.logic.core.CoreProp;
import com.logic.modular.game.player.hero.service.PlayerHeroServiceImpl;
import com.logic.modular.game.player.info.mapper.PlayerMapper;
import com.logic.modular.game.player.info.service.PlayerServiceImpl;
import com.logic.modular.game.player.skill.PlayerSkillServiceImpl;
import com.logic.modular.gameCore.config.service.impl.CoreConfigServiceImpl;
import com.logic.modular.gameCore.cultivate.prop.CorePropServiceImpl;
import lombok.extern.slf4j.Slf4j;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.apache.dubbo.config.annotation.DubboService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * 背包单元格(BagItem)表服务实现类
 * 操作物品需获取背包单元格操作权
 *
 * @author cxy
 * @since 2024-11-13 16:06:10
 */
@Slf4j
@Service

@DubboService(interfaceClass = BagItemService.class)
public class BagItemServiceImpl extends ServiceImpl<BagItemMapper, BagItem>
        implements BagItemService {
    @Resource
    private RedisUtils redisUtils;
    @Lazy
    @Resource
    private BagServiceImpl bagServiceImpl;
    @Autowired
    private CoreConfigServiceImpl coreConfigServiceImpl;
    @Autowired
    private PlayerMapper playerMapper;
    @Autowired
    private PlayerServiceImpl playerServiceImpl;
    @Autowired
    private PlayerHeroServiceImpl playerHeroServiceImpl;
    @Autowired
    private PlayerSkillServiceImpl playerSkillServiceImpl;

    @Override
    public boolean updateById(BagItem bagItem) {
        boolean b = bagItem.updateById();
        if (b) {
            bagItem = getById(bagItem.getId());
            saveCache(bagItem);
            MessageService.sendMessageToUser(bagItem.getUid(), MessageService.updateJsonObject("bagItem", bagItem));
        } else {
            String info = String.format("玩家:%d,背包单元格更新失败", bagItem.getUid());
            log.info(info);
        }
        return b;
    }

    public static void saveCache(BagItem byId) {
        String key1 = String.format(GameConstant.PLAYER_BAG_ITEM_KEY, byId.getUid());
        String key2 = byId.getId().toString();
        RedisUtils.redisUtils().hPut(key1, key2, byId, 30L, TimeUnit.DAYS);
    }


    public List<BagItem> list(Long playerId) {
        String key1 = String.format(GameConstant.PLAYER_BAG_ITEM_KEY, playerId);
        List<BagItem> collect = redisUtils.hValues(key1).stream().map(item -> (BagItem) item).collect(Collectors.toList());
        if (collect.isEmpty()) {
            collect = list(new LambdaQueryWrapper<BagItem>().eq(BagItem::getUid, playerId));
            for (BagItem bagItem : collect) {
                saveCache(bagItem);
            }
        }
        return collect;
    }


    public BagItem select(BagItem build) {
        BagItem res;
        Long uid = build.getUid();
        if (uid == null) {
            res = getOne(new LambdaQueryWrapper<>(build));
        } else {
            res = list(uid).stream().filter(bagItem -> BeanUtil.selector()
                    .eq(build.getCorePropId(), bagItem.getCorePropId())
                    .value()
            ).findFirst().orElse(getOne(new LambdaQueryWrapper<>(build)));
        }
        return res;
    }


    public List<BagItem> obtainBagItemList(
            Long playerId,
            Long corePropId,
            Integer status,
            Integer propStatus
    ) {
        String key1 = String.format(GameConstant.PLAYER_BAG_ITEM_KEY, playerId);
        List<BagItem> collect = redisUtils.hValues(key1).stream()
                .map(item -> (BagItem) item
                ).filter(item -> BeanUtil.selector()
                        .eq(playerId, item.getUid())
                        .eq(corePropId, item.getCorePropId())
                        .eq(status, item.getStatus())
                        .eq(propStatus, item.getPropStatus()).value()
                ).collect(Collectors.toList());
        if (collect.isEmpty()) {
            collect = list(new LambdaQueryWrapper<BagItem>()
                    .eq(BagItem::getUid, playerId)
                    .eq(BagItem::getCorePropId, corePropId)
                    .eq(BagItem::getStatus, status)
                    .eq(BagItem::getPropStatus, propStatus)
            );
        }

        return collect;
    }

    /**
     * 获取玩家背包的操作权
     *
     * @param playerId   玩家id
     * @param corePropId 道具id
     * @return List<BagItem> 可操作的背包单元格
     */
    public List<BagItem> obtainPlayerBagItemHandlerListPower(Long playerId, Long corePropId) {
        return list(new LambdaQueryWrapper<BagItem>()
                .eq(BagItem::getUid, playerId)
                .eq(BagItem::getCorePropId, corePropId)
                .in(BagItem::getStatus, 0, 1, -2)
        ).stream().peek(item -> {
            item.setStatus(3);
            updateById(item);
        }).collect(Collectors.toList());
    }

    /**
     * 获取玩家背包的操作权
     *
     * @param playerId  玩家id
     * @param bagItemId 道具id
     * @return 可操作的背包单元格
     */
    public BagItem obtainPlayerBagItemHandlerPower(Long playerId, Long bagItemId) {
        BagItem byId = getById(bagItemId);
        if (byId == null) {
            throw new ServiceException("背包单元格不存在");
        }
        if (!byId.getUid().equals(playerId)) {
            throw new ServiceException("异常参数");
        }
        if (byId.getStatus() == 3) {
            throw new ServiceException("点击过快请等待");
        }
        byId.setStatus(3);
        updateById(byId);
        return byId;
    }

    private List<BagItem> obtainEmptyBagItemList(Long playerId, Long corePropId) {
        List<BagItem> bagItems = obtainBagItemList(playerId, corePropId, 0, 0);
        if (bagItems.isEmpty()) {
            bagItems = new ArrayList<>();
        }
        return bagItems;
    }

    private BagItem obtainEmptyBagItem(Long playerId, Long corePropId) {
        BagItem emptyBagItem = obtainEmptyBagItemList(playerId, corePropId).stream().findFirst().orElse(null);
        if (emptyBagItem == null) {
            Bag bag = bagServiceImpl.obtainBag(playerId);
            if (bag.bagIsFull()) {
                throw new ServiceException("背包已满,请拓展背包");
            }
            emptyBagItem = new BagItem();
            emptyBagItem.setUid(playerId);
            if (emptyBagItem.insert()) {
                saveCache(emptyBagItem);
                bag.bagMessage.add(emptyBagItem.getId());
                bagServiceImpl.updateById(bag);
            }
        }
        emptyBagItem.init();
        emptyBagItem.setCorePropId(corePropId);
        emptyBagItem.extend.put("obtainTime", new Date());
        updateById(emptyBagItem);
        return emptyBagItem;
    }

    @Override
    @Transactional
    public boolean obtainBagItem(
            Long playerId,
            Long corePropId,
            BigDecimal obtainNum
    ) {
        // 获取当前物品的容量
        CoreProp cache = CorePropServiceImpl.getCache(corePropId);
        Long stack = cache.getStack();
        BigDecimal stackItem = BigDecimal.valueOf(stack);

        if (cache.getType() == 8) {// 英雄
            playerHeroServiceImpl.obtainHero(playerId, cache.getHeroId(), obtainNum.intValue());
        } else if (cache.getType() == 9) {
            playerSkillServiceImpl.obtainSkill(playerId, cache.getSkillId());
        }

        int i = stackItem.compareTo(BigDecimal.ZERO);
        if (i == 0) { // 无限堆
            BagItem one = getOne(new LambdaQueryWrapper<BagItem>()
                    .eq(BagItem::getCorePropId, corePropId)
                    .eq(BagItem::getUid, playerId)
            );
            if (one == null) {
                one = obtainEmptyBagItem(playerId, corePropId);
            }
            one.obtainHandlerPower();
            one.num = one.num == null ? obtainNum : one.num.add(obtainNum);
            one.setStatus(1);
            return updateById(one);
        } else if (i > 0) {
            List<BagItem> handlerPower = obtainPlayerBagItemHandlerListPower(playerId, corePropId);
            // 所有单元格的物品数量
            BigDecimal totalNum = handlerPower.stream()
                    .map(BagItem::getNum)
                    .reduce(BigDecimal.ZERO, BigDecimal::add)
                    .add(obtainNum);


            for (BagItem item : handlerPower) {// 填充已有的背包
                boolean value = totalNum.compareTo(BigDecimal.ZERO) <= 0;
                if (value) {// 以有的背包数量装完了
                    item.clear();
                    updateById(item);
                    continue;
                }
                totalNum = pickIntoBagItem(stackItem, totalNum, item);
            }

            while (totalNum.compareTo(BigDecimal.ZERO) > 0) {
                BagItem emptyBagItem = obtainEmptyBagItem(playerId, corePropId);
                emptyBagItem.setCorePropId(corePropId);
                emptyBagItem.setPropStatus(0);
                totalNum = pickIntoBagItem(stackItem, totalNum, emptyBagItem);
            }
            return true;
        }
        log.error("不支持的叠加类型");
        return false;
    }

    @Override
    public boolean obtainBagItemAll(Long corePropId, BigDecimal obtainNum) {
        for (Long l : playerServiceImpl.obtainPlayerIds()) {
            obtainBagItem(l, corePropId, obtainNum);
        }
        return true;
    }

    /**
     * 填充背包
     *
     * @param stackItem    单个物品的容量
     * @param totalNum     需要打包的数量
     * @param emptyBagItem 背包单元
     * @return 需要打包的数量
     */
    private BigDecimal pickIntoBagItem(BigDecimal stackItem, BigDecimal totalNum, BagItem emptyBagItem) {
        int comparedTo = stackItem.compareTo(totalNum);
        if (comparedTo > 0) { // 可以装下剩余数量
            emptyBagItem.setNum(totalNum);
            emptyBagItem.setStatus(1);
            totalNum = BigDecimal.ZERO;
        } else if (comparedTo == 0) {// 刚好装下剩余数量
            emptyBagItem.setNum(totalNum);
            emptyBagItem.setStatus(2);
            totalNum = BigDecimal.ZERO;
        } else { // 不够装下剩余数量
            emptyBagItem.setNum(stackItem);
            emptyBagItem.setStatus(2);
            totalNum = totalNum.subtract(stackItem);
        }
        updateById(emptyBagItem);
        return totalNum;
    }

    /**
     * 清除 物品
     *
     * @param playerId   玩家id
     * @param corePropId 物品id
     */
    @Transactional
    public void clearProp(Long playerId, Long corePropId) {
        // 所有单元格的物品数量
        BigDecimal totalNum = list(new LambdaQueryWrapper<BagItem>()
                .eq(BagItem::getUid, playerId)
                .eq(BagItem::getCorePropId, corePropId)
        ).stream()
                .map(BagItem::getNum)
                .reduce(BigDecimal.ZERO, BigDecimal::add);
        obtainBagItem(playerId, corePropId, totalNum.negate());
    }

    public void removeCache(Long playerId) {
        String key = String.format(GameConstant.PLAYER_BAG_ITEM_KEY, playerId);
        redisUtils.delete(key);
    }

    /**
     * 回收道具
     */
    @Transactional
    public Object recycleProp(Long playerId, Long playerBagItemId, int num) {
        BagItem byId = getById(playerBagItemId);
        if (byId == null) {
            return null;
        } else if (!Objects.equals(byId.getUid(), playerId)) {
            throw new ServiceException("不存在此道具");
        }
        Long corePropId = byId.getCorePropId();
        CoreProp coreProp = CorePropServiceImpl.getCache(corePropId);
        // 扣除
        obtainBagItem(playerId, corePropId, BigDecimal.valueOf(num).negate());
        // 发放回收获得
        coreProp.getRecoveryObtain().forEach((k, v) -> obtainBagItem(playerId, k, BigDecimal.valueOf((long) v * num)));
        return null;
    }

    public boolean checkPropNum(Long playerId, Long corePropId, BigDecimal num) {
        BigDecimal sum = list(playerId).stream()
                .filter(item -> item.getCorePropId().equals(corePropId))
                .map(BagItem::getNum)
                .reduce(BigDecimal.ZERO, BigDecimal::add);
        if (sum.compareTo(num) < 0) {
            log.error("背包内道具:{}不足,当前:{},需要:{}", corePropId, sum, num);
            return false;
        }
        return true;
    }

    /**
     * 质押物品
     */
    @Transactional
    public String pledgeProp(Long playerId, Map<Long, BigDecimal> needMoney, String keyInfo) {
        needMoney.forEach((corePropId, num) -> {
            if (!checkPropNum(playerId, corePropId, num)) {
                String name = CorePropServiceImpl.getCache(corePropId).getName();
                throw new ServiceException(String.format("%s不足", name));
            }
            obtainBagItem(playerId, corePropId, num.negate());
        });
        String pledgePropKey = String.format("pledgeProp-%s:%s", keyInfo, UUID.fastUUID());
        return Pledge.init(pledgePropKey, playerId, needMoney);
    }

    /**
     * 赎回物品
     */
    @Transactional
    public void redemptionOfProp(String pledgePropKey) {
        // 指定泛型参数并检查 Optional 是否存在值
        Pledge<?> redeem = Pledge.redeem(pledgePropKey)
                .orElse(null);
        if (redeem == null) {
            return;
        }
        Long playerId = redeem.getPlayerId();
        Map<Long, BigDecimal> pledgeProp = (Map<Long, BigDecimal>) redeem.getPledgeProp();
        pledgeProp.forEach((corePropId, num) -> obtainBagItem(playerId, corePropId, num));
    }

    /**
     * 放弃质押
     */
    public void abandoningPledge(String pledgePropKey) {
        Pledge.unPledge(pledgePropKey);
    }


    public boolean hasProp(Long playerId, Long i) {
        return !list(playerId).stream().noneMatch(item -> item.getCorePropId().equals(i));
    }
}
